﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Globalization;
using GraffitiExtras.Plugins.Waegis.WaegisClient.Components;
using GraffitiExtras.Plugins.Waegis.WaegisClient.Resources;

namespace GraffitiExtras.Plugins.Waegis.WaegisClient
{
    /// <summary>
    /// Used to manage the working process with Waegis API and its methods
    /// </summary>
    [Serializable]
    internal class WaegisClient
    {
        #region Fields

        [NonSerialized]
        private HttpClient _httpClient = null;

        static readonly string _version = typeof(HttpClient).Assembly.GetName().Version.ToString();
        const string initializeUrlFormat = "http://api.waegis.com/web/1.0/{0}/initialize";
        const string checkUrlFormat = "http://api.waegis.com/web/1.0/{0}/check";
        const string reportSpamUrlFormat = "http://api.waegis.com/web/1.0/{0}/report-spam";
        const string reportHamUrlFormat = "http://api.waegis.com/web/1.0/{0}/report-ham";

        Uri _initializeUrl = null;
        Uri _checkUrl = null;
        Uri _reportSpamUrl = null;
        Uri _reportHamUrl = null;

        #endregion

        #region Properties

        string _apiKey;
        /// <summary>
        /// The API key
        /// </summary>
        string ApiKey
        {
            get
            {
                return this._apiKey ?? string.Empty;
            }
            set
            {
                this._apiKey = value ?? string.Empty;
                SetServiceUrls();
            }
        }

        Uri _siteInstanceUrl;
        /// <summary>
        /// The site instance
        /// </summary>
        Uri SiteInstanceUrl
        {
            get
            {
                return this._siteInstanceUrl;
            }
            set
            {
                this._siteInstanceUrl = value;
            }
        }

        IWebProxy _proxy;
        /// <summary>
        /// The proxy object to be used for the requests
        /// </summary>
        IWebProxy Proxy
        {
            get
            {
                return this._proxy;
            }
            set
            {
                this._proxy = value;
            }
        }

        int _timeout = 20000;
        /// <summary>
        /// Timeout in miliseconds
        /// </summary>
        int Timeout
        {
            get
            {
                return this._timeout;
            }
            set
            {
                this._timeout = value;
            }
        }

        string _userAgent;
        /// <summary>
        /// The user agent
        /// </summary>
        string UserAgent
        {
            get
            {
                return this._userAgent;
            }
            set
            {
                this._userAgent = value;
            }
        }

        #endregion

        #region Public Constrcutors

        /// <summary>
        /// Public constructor
        /// </summary>
        /// <param name="apiKey">API key</param>
        /// <param name="siteInstance">Site instance</param>
        /// <param name="httpClient">HTTP client to use for requests</param>
        /// <param name="applicationName">The name of the application</param>
        /// <param name="applicationVersion">The version of the application</param>
        public WaegisClient(string apiKey, Uri siteInstance, HttpClient httpClient,
            string applicationName, string applicationVersion)
        {
            if (string.IsNullOrEmpty(apiKey))
                throw new ArgumentNullException("siteKey", Messages.ArgumentNull_String);

            if (siteInstance == null)
                throw new ArgumentNullException("siteInstance", Messages.ArgumentNull_Uri);

            if (httpClient == null)
                throw new ArgumentNullException("httpClient", Messages.ArgumentNull_Generic);

            this._apiKey = apiKey;
            this._siteInstanceUrl = siteInstance;
            this._httpClient = httpClient;
            this._userAgent = BuildUserAgent(applicationName, applicationVersion);

            SetServiceUrls();
        }

        /// <summary>
        /// Public constructor
        /// </summary>
        /// <param name="apiKey">API key</param>
        /// <param name="siteInstance">Site instance</param>
        /// <param name="applicationName">The name of the application</param>
        /// <param name="applicationVersion">The version of the application</param>
        public WaegisClient(string apiKey, Uri siteInstance,
            string applicationName, string applicationVersion)
            : this(apiKey, siteInstance, new HttpClient(), applicationName, applicationVersion)
        {
        }

        /// <summary>
        /// Public constructor
        /// </summary>
        /// <param name="apiKey">API key</param>
        /// <param name="siteInstance">Site instance</param>
        public WaegisClient(string apiKey, Uri siteInstance)
            : this(apiKey, siteInstance, new HttpClient(), "Waegis .NET", _version)
        {
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Initializes a site instance
        /// </summary>
        /// <returns>A Boolean value specifying if the site instance if valid</returns>
        public bool InitializeSiteInstance()
        {
            string postData = WaegisSerializer.SerializeSiteInstance(this._siteInstanceUrl.ToString());

            string serviceOutput = this._httpClient.PutRequest(this._initializeUrl, this._userAgent, this._timeout, postData);

            ServiceResult result = WaegisDeserializer.GetResultFromData(serviceOutput);

            if (result == null)
                throw new ResponseException("Waegis returned an empty response");

            if (result.Status == ResultStatus.Success)
                return string.Equals("valid", result.Message, StringComparison.InvariantCultureIgnoreCase);
            else
                throw new ApplicationException(result.Message);
        }

        /// <summary>
        /// Checks a post for being spam or ham
        /// </summary>
        /// <param name="post">The post</param>
        /// <returns>A Boolean value specifying if the post is spam or not</returns>
        public bool CheckPost(Post post)
        {
            DateTime start = DateTime.Now;

            string postData = WaegisSerializer.SerializePost(this._siteInstanceUrl.ToString(), post);

            string serviceOutput = this._httpClient.PutRequest(this._checkUrl, this._userAgent, this._timeout, postData);

            ServiceResult result = WaegisDeserializer.GetResultFromData(serviceOutput);

            DateTime now = DateTime.Now;

            TimeSpan diff = now - start;

            if (result == null)
                throw new ResponseException("Waegis returned an empty response");

            if (result.Status == ResultStatus.Failure)
                throw new ResponseException
                    (string.Format(CultureInfo.InvariantCulture, result.Message));

            if (result.Message == "spam")
                return true;
            else
                return false;
        }

        /// <summary>
        /// Reports a ham
        /// </summary>
        /// <param name="post">The post</param>
        /// <returns>A Boolean value indicating if the operation was successful</returns>
        public bool ReportHam(Post post)
        {
            string postData = WaegisSerializer.SerializePost(this._siteInstanceUrl.ToString(), post);

            string serviceOutput = this._httpClient.PutRequest(this._reportHamUrl, this._userAgent, this._timeout, postData);

            ServiceResult result = WaegisDeserializer.GetResultFromData(serviceOutput);

            if (result == null)
                throw new ResponseException("Waegis returned an empty response");

            if (result.Status == ResultStatus.Failure)
                return false;

            return true;
        }

        /// <summary>
        /// Reports a spam
        /// </summary>
        /// <param name="post">The post</param>
        /// <returns>A Boolean value indicating if the operation was successful</returns>
        public bool ReportSpam(Post post)
        {
            string postData = WaegisSerializer.SerializePost(this._siteInstanceUrl.ToString(), post);

            string serviceOutput = this._httpClient.PutRequest(this._reportSpamUrl, this._userAgent, this._timeout, postData);

            ServiceResult result = WaegisDeserializer.GetResultFromData(serviceOutput);

            if (result == null)
                throw new ResponseException("Waegis returned an empty response");

            if (result.Status == ResultStatus.Failure)
                return false;

            return true;
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Sets the API urls
        /// </summary>
        void SetServiceUrls()
        {
            this._initializeUrl = new Uri(String.Format(CultureInfo.InvariantCulture, initializeUrlFormat, this._apiKey));
            this._checkUrl = new Uri(String.Format(CultureInfo.InvariantCulture, checkUrlFormat, this._apiKey));
            this._reportHamUrl = new Uri(String.Format(CultureInfo.InvariantCulture, reportHamUrlFormat, this._apiKey));
            this._reportSpamUrl = new Uri(String.Format(CultureInfo.InvariantCulture, reportSpamUrlFormat, this._apiKey));
        }

        /// <summary>
        /// Builds the user agent string
        /// </summary>
        /// <param name="applicationName">The name of the application</param>
        /// <param name="applicationVersion">The version of the application</param>
        /// <returns>User agent</returns>
        private string BuildUserAgent(string applicationName, string applicationVersion)
        {
            return string.Format(CultureInfo.InvariantCulture,
                "Waegis .NET {0} / {1} {2} / Waegis 1.0", _version, applicationName, applicationVersion);
        }

        #endregion
    }
}
